const zeros = require('zeros');

const clamp = (value, minimum, maximum) => Math.max(Math.min(value, maximum), minimum);

/**
 * Warps an image according to a set of source and target coordinates
 * @kind function
 * @param {ndarray} input An ndarray of the image to warp
 * @param {array} displacements An array of [fromX, fromY, toX, toY] arrays
 * @param {number} [power=1] Shepard's power parameter
 * @returns {ndarray} The warped image
 */

const warp = (input, displacements, power = 1) => {
  const result = zeros([input.shape[0], input.shape[1], 3]);

  for (let y = 0; y < result.shape[1]; y += 1) {
    for (let x = 0; x < result.shape[0]; x += 1) {
      let warpedX = 0;
      let warpedY = 0;
      let denominator = 0;

      displacements.forEach(displacement => {
        let weight =
          (x - displacement[2]) * (x - displacement[2]) +
          (y - displacement[3]) * (y - displacement[3]);
        weight **= power;
        weight = weight < 1.0 ? 1.0 : 1.0 / weight;

        warpedX += (displacement[0] - displacement[2]) * weight;
        warpedY += (displacement[1] - displacement[3]) * weight;
        denominator += weight;
      });

      warpedX /= denominator;
      warpedY /= denominator;
      warpedX += x;
      warpedY += y;
      warpedX = Math.round(clamp(warpedX, 0, input.shape[0] - 1));
      warpedY = Math.round(clamp(warpedY, 0, input.shape[1] - 1));

      const red = input.get(warpedX, warpedY, 0);
      const green = input.get(warpedX, warpedY, 1);
      const blue = input.get(warpedX, warpedY, 2);

      result.set(x, y, 0, red);
      result.set(x, y, 1, green);
      result.set(x, y, 2, blue);
    }
  }

  return result;
};

module.exports = warp;
